/* 
* Copyright (c) 2009, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Linq;
using System.Threading;
using Bio.Data;
using Bio.Data.Interfaces;
using JulMar.Windows.Mvvm;
using System.Collections.Generic;
using Bio.Views.Alignment.Models;
using System.Diagnostics;

namespace Bio.Views.Alignment.ViewModels
{
    /// <summary>
    /// This represents a single row (sequence) in the data set which is data bound to the View.
    /// </summary>
    class RowViewModel : ViewModel
    {
        /// <summary>
        /// Entity being represented by this row
        /// </summary>
        private IAlignedBioEntity _entity;
        
        /// <summary>
        /// Parent AlignmentViewModel
        /// </summary>
        private readonly AlignmentViewModel _parentViewModel;
        
        /// <summary>
        /// Text to display in the header of the row
        /// </summary>
        private string _header;
        public string Header
        {
            get { return _header; }
            set
            {
                if (string.Compare(_header, value, true) != 0)
                {
                    _header = value;
                    OnPropertyChanged("Header");
                }
            }
        }

        /// <summary>
        /// Tooltip to display in the header of the row
        /// </summary>
        private string _tooltip;
        public string ToolTip
        {
            get { return _tooltip;  }    
            set
            {
                if (string.Compare(_tooltip, value, true) != 0)
                {
                    _tooltip = value;
                    OnPropertyChanged("ToolTip");
                }
            }
        }

        /// <summary>
        /// True if this is as grouping header
        /// </summary>
        private bool _isgroupHeader;
        public bool IsGroupHeader
        {
            get { return _isgroupHeader; }    
            set
            {
                if (_isgroupHeader != value)
                {
                    _isgroupHeader = value;
                    OnPropertyChanged("IsGroupHeader");
                }
            }
        }

        /// <summary>
        /// Cells
        /// </summary>
        public List<CellViewModel> Cells { get; set; }

        /// <summary>
        /// True if this row is locked and cannot be scrolled
        /// </summary>
        private bool _isLocked;
        public bool IsLocked
        {
            get { return _isLocked; }
            set
            {
                if (_isLocked == value)
                    return;

                _isLocked = value; 
                if (_isLocked == false)
                    IsReferenceSequence = false;

                OnPropertyChanged("IsLocked");
                _parentViewModel.OnRowLocked(this);
            }
        }

        /// <summary>
        /// This is the brush used to color the background when this is a reference sequence.
        /// </summary>
        private string _backgroundBrush;
        public string BackgroundBrush
        {
            get { return string.IsNullOrEmpty(_backgroundBrush) ? "Transparent" : _backgroundBrush; }
            set { _backgroundBrush = value; OnPropertyChanged("BackgroundBrush"); }
        }

        /// <summary>
        /// True if this is the reference sequence - also implies that it is locked.
        /// </summary>
        private bool _isReferenceSequence;
        public bool IsReferenceSequence
        {
            get { return _isReferenceSequence; }
            set
            {
                if (value == true && !_isLocked)
                    IsLocked = true;

                _isReferenceSequence = value;

                if (_isReferenceSequence == true)
                {
                    int count = _parentViewModel.Rows.Where(rvm => rvm.IsReferenceSequence).Count() - 1;
                    Debug.Assert(count >= 0);
                    BackgroundBrush = _parentViewModel.ReferenceColors[count%_parentViewModel.ReferenceColors.Count];
                }
                else
                {
                    BackgroundBrush = null;
                }

                _parentViewModel.ChangeReferenceSequenceSelection(this);

                // Set the background color on all the cells
                Cells.ForEach(cell => cell.BackgroundBrush = BackgroundBrush);
                OnPropertyChanged("IsReferenceSequence");
            }
        }

        /// <summary>
        /// Constructor for the row
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="columns"></param>
        public RowViewModel(AlignmentViewModel parent, int columns)
        {
            if (parent == null)
                throw new ArgumentNullException("parent");

            _parentViewModel = parent;
            Cells = new List<CellViewModel>(columns);
            for (int i = 0; i < columns; i++)
                Cells.Add(new CellViewModel(this, i));
        }

        /// <summary>
        /// This prepares the row for updates.
        /// </summary>
        /// <param name="columnCount">New column count</param>
        public void ResetColumnCount(int columnCount)
        {
            if (Cells.Count < columnCount)
            {
                int create = columnCount - Cells.Count;
                for (int i = 0; i < create; i++)
                    Cells.Add(new CellViewModel(this, Cells.Count));
            }
            else if (Cells.Count > columnCount)
                Cells.RemoveRange(columnCount - 1, Cells.Count - columnCount);
        }

        /// <summary>
        /// This is called to update the row contents.
        /// </summary>
        /// <param name="seq">New sequence of data</param>
        /// <param name="start">Starting column number to display</param>
        /// <param name="count">Column count to display</param>
        public void UpdateRow(IAlignedBioEntity seq, int start, int count)
        {
            IsLocked = false;

            if (seq != null)
            {
                _entity = seq;
                IsGroupHeader = _entity is GroupHeader;
                Header = seq.Entity.ScientificName;
                ToolTip = seq.Entity.TaxonomyId;
                UpdateRow(start, count);
            }
            else if (_entity != null)
            {
                _entity = null;
                Header = string.Empty;
                IsGroupHeader = false;
                Parallel.ForEach(Cells, cell => cell.Update(null, null));
            }
        }

        private string GetReferenceSequenceBrush(IBioSymbol newValue, int col)
        {
            if (newValue != null && !newValue.Equals(BioSymbol.Gap) && !newValue.Equals(BioSymbol.None))
            {
                var refSeqs = _parentViewModel.Rows.Where(rvm => rvm.IsLocked);
                foreach (var seq in refSeqs)
                {
                    if (seq != null)
                    {
                        if (newValue.Equals(seq.Cells[col].Value))
                            return seq.BackgroundBrush;
                    }
                }
            }
            return null;
        }

        public void NewReferenceSequenceSelected(RowViewModel model)
        {
            Parallel.For(0, Cells.Count, i =>
                {
                    Cells[i].BackgroundBrush = GetReferenceSequenceBrush(Cells[i].Value, i);
                });
        }

        public void UpdateRow(int start, int count)
        {
            if (_entity == null)
                return;

            if (_entity.AlignedData != null)
            {
                count = Math.Min(count, Cells.Count);
                Parallel.For(0, count, i =>
                {
                    var newValue = _entity.AlignedData[i + start];
                    CellViewModel cvm = Cells[i];
                    cvm.Update(newValue, GetReferenceSequenceBrush(newValue, i));
                });
            }
            else
                Parallel.ForEach(Cells, cell => cell.Update(null, null));
        }
    }
}